using System;
using System.Collections;
using System.Collections.Generic;


namespace MyFramework
{
    public class PETimer
    {
        private static readonly string obj = "";
        private Action<string> LogInfo;
        private int tID = 0;
        private List<int> tIdList=new List<int>();
        
        private List<PETimeTask> tmpTimeTasks=new List<PETimeTask>();
        private List<PETimeTask> taskTime=new List<PETimeTask>();
        private List<int> recTidList=new List<int>();

        private int FrameCount = 0;
        private List<PEFrametask> tmpFrameTask=new List<PEFrametask>();
        private List<PEFrametask> Frametask=new List<PEFrametask>();
        
        private DateTime startTime=new DateTime(1970,1,1,0,0,0,0);
        private double NowTime;
        public PETimer()
        {
            tIdList.Clear();
            tmpTimeTasks.Clear();
            taskTime.Clear();
            recTidList.Clear();
            
            tmpFrameTask.Clear();
            Frametask.Clear();
        }

        public void Update()
        {
           
            CheckTimeTask();
            CheackFrameTask();
            if (recTidList.Count>0)
            {
                RecycleTid();
            }
        }
        #region TimeTask

        private void CheckTimeTask()
        {
            for (int index=0;index<tmpTimeTasks.Count;index++)
            {
                taskTime.Add(tmpTimeTasks[index]);
            }
            tmpTimeTasks.Clear();
            NowTime = GetUTCMillisceond();
            for (int index=0;index<taskTime.Count;index++)
            {
                PETimeTask timeTask = taskTime[index];
                if (NowTime<timeTask.delayTime)
                    continue;
                else
                {
                    Action cb = timeTask.callBack;
                    if (cb!=null)
                    {
                        cb();
                    }
                }
                if (timeTask.count==1)
                {
                    taskTime.RemoveAt(index);
                    index--;
                    recTidList.Add(timeTask.tId);
                }
                else
                {
                    if (timeTask.count != 0)
                        timeTask.count--;
                    timeTask.delayTime += timeTask.delay;
                }
            }
        }

        private void CheackFrameTask()
        {
            for (int index=0;index<tmpFrameTask.Count;index++)
            {
                Frametask.Add(tmpFrameTask[index]);
            }
            if (FrameCount==int.MaxValue)
            {
                FrameCount = 0;
            }
            FrameCount += 1;
            tmpFrameTask.Clear();
            for (int index=0;index<Frametask.Count;index++)
            {
                PEFrametask frametask = Frametask[index];
                if (FrameCount < frametask.FrameTime)
                    continue;
                else
                {
                    Action cb = frametask.callBack;
                    if (cb!=null)
                    {
                        cb();
                    }
                }
                if (frametask.count==1)
                {
                    Frametask.RemoveAt(index);
                    index--;
                    recTidList.Add(frametask.tId);
                }
                else
                {
                    if (frametask.count != 0)
                        frametask.count--;
                    frametask.FrameTime += frametask.delay;
                }
            }
        }

        /// <summary>
        /// 增加定时任务
        /// </summary>
        /// <param name="delayTime"></param>
        /// <param name="callBack"></param>
        /// <param name="timeUnit"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public int AddTimeTask(double delayTime, Action callBack,PETimeUnit timeUnit=PETimeUnit.MilliSecond,int count=1)
        {
            if (timeUnit!=PETimeUnit.MilliSecond)
            {
                switch (timeUnit)
                {
                    case PETimeUnit.Second:
                        delayTime = delayTime * 1000;
                        break;
                    case PETimeUnit.Minute:
                        delayTime = delayTime * 1000 * 60;
                        break;
                    case PETimeUnit.Hour:
                        delayTime = delayTime * 1000 * 60 * 60;
                        break;
                    case PETimeUnit.Day:
                        delayTime = delayTime * 1000 * 60 * 60 * 24;
                        break;
                    default:
                        Log("TimeUnit error");
                        break;
                }
            }

            NowTime = GetUTCMillisceond();
            int tId = GetId();
            tmpTimeTasks.Add(new PETimeTask(tId,callBack,NowTime+delayTime,count,delayTime));
            tIdList.Add(tId);
            return tId;
        }
        /// <summary>
        /// 删除定时任务
        /// </summary>
        /// <param name="tId"></param>
        /// <returns></returns>
        public bool DelTimeTask(int tId)
        {
            bool isExit = false;
            for (int index=0;index<taskTime.Count;index++)
            {
                PETimeTask task = taskTime[index];
                if (task.tId==tId)
                {
                    taskTime.RemoveAt(index);
                    for (int i=0;i<tIdList.Count;i++)
                    {
                        if (tId==tIdList[i])
                        {
                            tIdList.RemoveAt(i);
                            break;
                        }
                    }
                    isExit = true;
                    break;
                }
            }

            if (!isExit)
            {
                for (int i=0;i<tmpTimeTasks.Count;i++)
                {
                    PETimeTask task = tmpTimeTasks[i];
                    if (task.tId==tId)
                    {
                        tmpTimeTasks.RemoveAt(i);
                        for (int j=0;j<tIdList.Count;j++)
                        {
                            if (tId==tIdList[j])
                            {
                                tIdList.RemoveAt(j);
                                break;
                            }
                        }
                        isExit = true;
                        break;
                    }
                }
            }
            return isExit;
        }
        /// <summary>
        /// 替换定时任务
        /// </summary>
        /// <param name="tId"></param>
        /// <param name="delayTime"></param>
        /// <param name="callBack"></param>
        /// <param name="timeUnit"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public bool ReplaceTask(int tId, double delayTime, Action callBack, PETimeUnit timeUnit = PETimeUnit.MilliSecond,
            int count = 1)
        {
            bool Res = false;
            if (timeUnit != PETimeUnit.MilliSecond)
            {
                switch (timeUnit)
                {
                    case PETimeUnit.Second:
                        delayTime = delayTime * 1000;
                        break;
                    case PETimeUnit.Minute:
                        delayTime = delayTime * 1000 * 60;
                        break;
                    case PETimeUnit.Hour:
                        delayTime = delayTime * 1000 * 60 * 60;
                        break;
                    case PETimeUnit.Day:
                        delayTime = delayTime * 1000 * 60 * 60 * 24;
                        break;
                    default:
                        Log("TimeUnit error");
                        break;
                }
            }
        
            NowTime = GetUTCMillisceond();
            double destTime = NowTime + delayTime;
            PETimeTask task = new PETimeTask(tId, callBack, destTime, count, delayTime);
            for (int i = 0; i < taskTime.Count; i++)
            {
                if (tId == taskTime[i].tId)
                {
                    taskTime[i] = task;
                    Res = true;
                    break;
                }
            }

            if (!Res)
            {
                for (int i=0;i<tmpTimeTasks.Count;i++)
                {
                    if (tmpTimeTasks[i].tId==tId)
                    {
                        tmpTimeTasks[i] = task;
                        Res = true;
                        break;
                    }
                }
            }
            return Res;
        }

        #endregion

        #region ToolTask

        /// <summary>
        /// 获取定时任务ID
        /// </summary>
        /// <returns></returns>
        private int GetId()
        {
            lock (obj)
            {
                tID += 1;
                
                //安全保护
                while (true)
                {
                    if (tID == int.MaxValue)
                    {
                        tID = 0;
                    }
                    bool isUsed = false;
                    for (int index = 0; index < tIdList.Count; index++)
                    {
                        if (tID == tIdList[index])
                        {
                            isUsed = true;
                            break;
                        }
                    }
                    if (isUsed)
                    {
                        tID += 1;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return 0;
        }
        /// <summary>
        /// 清理timeId
        /// </summary>
        private void RecycleTid()
        {
            for (int i=0;i<recTidList.Count;i++)
            {
                int tid = recTidList[i];
                for (int j=0;j<tIdList.Count;j++)
                {
                    if (tid==tIdList[i])
                    {
                        tIdList.RemoveAt(j);
                        break;
                    }
                }
            }
            recTidList.Clear();
        }
        /// <summary>
        /// 设置日志回调函数
        /// </summary>
        /// <param name="log"></param>
        public void SetLog(Action<string> log)
        {
            LogInfo = log;
        }

        private void Log(string info)
        {
            if (LogInfo != null)
                LogInfo(info);
        }

        private double GetUTCMillisceond()
        {
            TimeSpan ts = DateTime.UtcNow - startTime;
            return ts.TotalMilliseconds;
        }

        #endregion


        #region FrameTask

        public int AddTrameTask(int delayTime, Action callBack,int count=1)
        {
            int tId = GetId();
            tmpFrameTask.Add(new PEFrametask(tId,callBack,FrameCount+delayTime,count,delayTime));
            tIdList.Add(tId);
            return tId;
        }

        public bool DelFrameTask(int tId)
        {
            bool isExit = false;
            for (int index=0;index<Frametask.Count;index++)
            {
                PEFrametask task = Frametask[index];
                if (task.tId==tId)
                {
                    Frametask.RemoveAt(index);
                    for (int i=0;i<tIdList.Count;i++)
                    {
                        if (tId==tIdList[i])
                        {
                            tIdList.RemoveAt(i);
                            break;
                        }
                    }
                    isExit = true;
                    break;
                }
            }

            if (!isExit)
            {
                for (int i=0;i<tmpFrameTask.Count;i++)
                {
                    PEFrametask task = tmpFrameTask[i];
                    if (task.tId==tId)
                    {
                        tmpFrameTask.RemoveAt(i);
                        for (int j=0;j<tIdList.Count;j++)
                        {
                            if (tId==tIdList[j])
                            {
                                tIdList.RemoveAt(j);
                                break;
                            }
                        }
                        isExit = true;
                        break;
                    }
                }
            }
            return isExit;
        }

        public bool ReplaceFrameTask(int tId, int delayTime, Action callBack,
            int count = 1)
        {
            bool Res = false;

            int destTime = FrameCount + delayTime;
            PEFrametask task = new PEFrametask(tId, callBack, destTime, count, delayTime);
            for (int i = 0; i < Frametask.Count; i++)
            {
                if (tId == Frametask[i].tId)
                {
                    Frametask[i] = task;
                    Res = true;
                    break;
                }
            }

            if (!Res)
            {
                for (int i=0;i<tmpFrameTask.Count;i++)
                {
                    if (tmpFrameTask[i].tId==tId)
                    {
                        tmpFrameTask[i] = task;
                        Res = true;
                        break;
                    }
                }
            }

            return Res;
        }

        #endregion
    }
    public class PETimeTask
    {
        public int tId;
        public double delayTime;
        public Action callBack;
        public int count;
        public double delay;
        
        public PETimeTask(int tId,Action callBack,double delayTime,int count,double delay)
        {
            this.tId = tId;
            this.callBack = callBack;
            this.delayTime = delayTime;
            this.count = count;
            this.delay = delay;
        }
    }

    public class PEFrametask
    {
        public int tId;
        public int FrameTime;
        public Action callBack;
        public int count;
        public int delay;
        
        public PEFrametask(int tId,Action callBack,int FrameTime,int count,int delay)
        {
            this.tId = tId;
            this.callBack = callBack;
            this.FrameTime = FrameTime;
            this.count = count;
            this.delay = delay;
        }
    }

    public enum PETimeUnit
    {
        MilliSecond,
        Second,
        Minute,
        Hour,
        Day
    }
}